#include "stdafx.h"
#include "FileInfo.h"
#include "md5.h"


IMPLEMENT_DYNAMIC(CFileInfo, CObject);

CFileInfo::CFileInfo()
{
	m_bInited = FALSE;
	memset(m_arrMD5, 0, sizeof(m_arrMD5));
}

CFileInfo::CFileInfo(CFileInfo& obj)
{
	m_bInited = obj.m_bInited;
	m_fsStatus = obj.m_fsStatus;
	m_bIsFolder = obj.m_bIsFolder;
	memcpy(m_arrMD5, obj.m_arrMD5, sizeof(m_arrMD5));
}

CFileInfo::~CFileInfo()
{
}

BOOL CFileInfo::ReadFromFile(LPCTSTR lpszFilePathName)
{
	CFile file;

	if (!file.Open(lpszFilePathName, CFile::modeRead|CFile::shareDenyWrite|CFile::osSequentialScan))
	{
		return FALSE;
	}

	return ReadFromFile(file);
}

BOOL CFileInfo::ReadFromFile(CFile& file)
{
    m_bInited = FALSE;
    m_bIsFolder = FALSE;

    if (!file.GetStatus(m_fsStatus))
    {
        return FALSE;
    }

    GetFileMD5(file, m_arrMD5);

    m_bInited = TRUE;

    return TRUE;
}

BOOL CFileInfo::ReadFromFolder(LPCTSTR lpszFolderPathName)
{
	m_bInited = FALSE;
	m_bIsFolder = TRUE;

	CFile::GetStatus(lpszFolderPathName, m_fsStatus);
	memset(m_arrMD5, 0, sizeof(m_arrMD5));

	m_bInited = TRUE;

	return TRUE;
}

void CFileInfo::UpdateChangeTime(const CTime newTime)
{
	this->m_fsStatus.m_mtime = newTime;
}

int CFileInfo::operator==(const CFileInfo& obj)
{
	if (m_bInited != obj.m_bInited)
	{
		return 0;
	}

	if (*this != obj.m_fsStatus || m_bIsFolder != obj.m_bIsFolder)
	{
		return 0;
	}

	return (memcmp(m_arrMD5, obj.m_arrMD5, sizeof(m_arrMD5)) == 0);
}

int CFileInfo::operator!=(const CFileInfo& obj)
{
	return !(*this == obj);
}

int CFileInfo::operator==(const CFileStatus& status)
{
	if (!m_bInited)
	{
		return 0;
	}

	if ((m_bIsFolder == ((status.m_attribute | CFile::directory) != 0)) && m_fsStatus.m_mtime == status.m_mtime && m_fsStatus.m_size == m_fsStatus.m_size)
	{
		return 1;
	}

	return 0;
}

int CFileInfo::operator!=(const CFileStatus& status)
{
	return !(*this == status);
}

int CFileInfo::operator==(CFile& file)
{
	CFileStatus status;
	BYTE arrMD5[16];

	if (!m_bInited || m_bIsFolder)
	{
		return 0;
	}

	if (!file.GetStatus(status))
	{
		return 0;
	}

	GetFileMD5(file, arrMD5);

	if (memcmp(arrMD5, m_arrMD5, sizeof(m_arrMD5)) == 0)
	{
		return 1;
	}

	return 0;
}

int CFileInfo::operator!=(CFile& file)
{
	return !(*this == file);
}

int CFileInfo::operator==(const WIN32_FIND_DATA& finddata)
{
	ULONGLONG ullFileSize;

	if (!m_bInited)
	{
		return 0;
	}

    if (m_bIsFolder != ((finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0))
    {
        return 0;
    }

	ullFileSize = ((ULONGLONG)finddata.nFileSizeHigh << 32) | finddata.nFileSizeLow;
	if (m_fsStatus.m_mtime == finddata.ftLastWriteTime && m_fsStatus.m_size == ullFileSize)
	{
		return 1;
	}

	return 0;
}

int CFileInfo::operator!=(const WIN32_FIND_DATA& finddata)
{
	return !(*this == finddata);
}

void CFileInfo::Serialize(CArchive& ar)
{
	if (ar.IsStoring())
	{
        ar << m_bInited;
        ar << m_bIsFolder;
		ar << m_fsStatus.m_ctime << m_fsStatus.m_mtime << m_fsStatus.m_atime << m_fsStatus.m_size << m_fsStatus.m_attribute << m_fsStatus._m_padding << CString(m_fsStatus.m_szFullName);
		ar << m_arrMD5[0] << m_arrMD5[1] << m_arrMD5[2] << m_arrMD5[3] << m_arrMD5[4] << m_arrMD5[5] << m_arrMD5[6] << m_arrMD5[7] << m_arrMD5[8] << m_arrMD5[9] << m_arrMD5[10] << m_arrMD5[11] << m_arrMD5[12] << m_arrMD5[13] << m_arrMD5[14] << m_arrMD5[15];
	}
	else
	{
        ar >> m_bInited;
        ar >> m_bIsFolder;
		ar >> m_fsStatus.m_ctime >> m_fsStatus.m_mtime >> m_fsStatus.m_atime >> m_fsStatus.m_size >> m_fsStatus.m_attribute >> m_fsStatus._m_padding >> CString(m_fsStatus.m_szFullName);
		ar >> m_arrMD5[0] >> m_arrMD5[1] >> m_arrMD5[2] >> m_arrMD5[3] >> m_arrMD5[4] >> m_arrMD5[5] >> m_arrMD5[6] >> m_arrMD5[7] >> m_arrMD5[8] >> m_arrMD5[9] >> m_arrMD5[10] >> m_arrMD5[11] >> m_arrMD5[12] >> m_arrMD5[13] >> m_arrMD5[14] >> m_arrMD5[15];
	}
}

void CFileInfo::GetFileMD5(CFile& file, BYTE* pMD5)
{
	MD5_CTX md5;
	ULONGLONG ullCurrentLength = 0;
	ULONGLONG ullFileLength;
	BYTE arrBuffer[32*1024];
	UINT uiBytesOfRead;
	ULONGLONG ullOrginalPos;

	ullOrginalPos = file.GetPosition();
	MD5Init(&md5);
	file.SeekToBegin();
	ullFileLength = file.GetLength();
	while (ullCurrentLength < ullFileLength)
	{
		uiBytesOfRead = file.Read(arrBuffer, sizeof(arrBuffer));
		ullCurrentLength += uiBytesOfRead;
		MD5Update(&md5, arrBuffer, uiBytesOfRead);
	}
	MD5Final(&md5);
	file.Seek(ullOrginalPos, CFile::begin);

	memcpy(pMD5, md5.digest, sizeof(md5.digest));
}